package mint.modules.generator.expression;

import java.util.ArrayList;
import java.util.Formatter;
import java.util.List;

import org.antlr.runtime.tree.Tree;

import mint.modules.generator.Scope;
import mint.modules.type.Type;

public class MultiplicativeExprNode extends Expression {
	private Expression left, right;
	private Type type;
	private int line;
	private List<String> errors;
	private String operator;

	public MultiplicativeExprNode(Scope theParent, ExpressionType exprType, Tree multiplicativeTree) {
		super(theParent, exprType);
		
		type = null;
		line = multiplicativeTree.getLine();
		errors = new ArrayList<String>();
		operator = null;
		
		if (multiplicativeTree.getText().equals("*") || 
			multiplicativeTree.getText().equals("/") || 
			multiplicativeTree.getText().equals("%"))
		{
			left = new MultiplicativeExprNode(theParent, exprType, multiplicativeTree.getChild(0));
			right = new UnaryExprNode(theParent, exprType, multiplicativeTree.getChild(1));
			operator = multiplicativeTree.getText();
		}
		else
		{
			left = new UnaryExprNode(theParent, exprType, multiplicativeTree);
			right = null;
		}
	}

	@Override
	public void testSemantic() {
		left.testSemantic();
		type = left.getType();
		
		if (right != null)
		{
			if (getExpressionType() != ExpressionType.Right)
				errors.add(line + ": " + operator + " must be used on the right side of a expression");
			right.testSemantic();
			
			Type typeLeft = left.getType();
			Type typeRight = right.getType();			
			
			if (operator.equals("*") || operator.equals("/"))
			{
				if (typeLeft.equals(Type.intType))
				{
					if (typeRight.equals(Type.intType))
						type = Type.intType;
					else if (typeRight.equals(Type.floatType))
						type = Type.floatType;
					else
					{
						type = Type.intType;
						errors.add(line + ": " + operator + " must be used on integer or float expressions");
					}
				}
				else if (typeLeft.equals(Type.floatType))
				{
					if (typeRight.equals(Type.intType))
						type = Type.floatType;
					else if (typeRight.equals(Type.floatType))
						type = Type.floatType;
					else
					{
						type = Type.intType;
						errors.add(line + ": " + operator + " must be used on integer or float expressions");
					}
				}
				else
				{
					type = Type.intType;
					errors.add(line + ": " + operator + " must be used on integer or float expressions");
				}
			} else if (operator.equals("%"))
			{
				if (typeLeft.equals(Type.intType))
				{
					if (typeRight.equals(Type.intType))
						type = Type.intType;
					else
					{
						type = Type.intType;
						errors.add(line + ": " + operator + " must be used on integer expressions");
					}
				} else
				{
					type = Type.intType;
					errors.add(line + ": " + operator + " must be used on integer expressions");
				}
			}
		}
	}

	@Override
	public List<String> getSemanticErrors() {
		errors.addAll(left.getSemanticErrors());
		if (right != null)
			errors.addAll(right.getSemanticErrors());
		return errors;
	}

	@Override
	public void emit(Formatter output) {
		left.emit(output);
		
		if (operator != null)
		{
			Type typeLeft = left.getType();
			Type typeRight = right.getType();			
			
			if (operator.equals("*"))
			{
				if (typeLeft.equals(Type.intType))
				{
					if (typeRight.equals(Type.intType))
					{
						right.emit(output);
						output.format("imul\n");
					}
					else if (typeRight.equals(Type.floatType))
					{
						output.format("i2f\n");
						right.emit(output);
						output.format("fmul\n");						
					}
				}
				else if (typeLeft.equals(Type.floatType))
				{
					right.emit(output);
					if (typeRight.equals(Type.intType))
						output.format("i2f\n");
					output.format("fmul\n");
				}
			} if (operator.equals("/"))	{
				if (typeLeft.equals(Type.intType))
				{
					if (typeRight.equals(Type.intType))
					{
						right.emit(output);
						output.format("idiv\n");
					}
					else if (typeRight.equals(Type.floatType))
					{
						output.format("i2f\n");
						right.emit(output);
						output.format("fdiv\n");						
					}
				}
				else if (typeLeft.equals(Type.floatType))
				{
					right.emit(output);
					if (typeRight.equals(Type.intType))
						output.format("i2f\n");
					output.format("fdiv\n");
				}
			} else if (operator.equals("%"))
			{
				right.emit(output);
				output.format("irem\n");
			}
		}
	}

	@Override
	public int getLine() {
		return line;
	}

	@Override
	public Type getType() {		
		return type;
	}

	@Override
	public void leftEmit(Formatter output) {
		left.leftEmit(output);		
	}

}
